#include "log.hpp"

using namespace mycode;
using std::to_string;

// 获取日期字符串
string getDatestr()
{
    char buff[80];
    time_t tim = time(nullptr);
    struct tm *st_tm = localtime(&tim);
    strftime(buff, 80, "%Y-%m-%d", st_tm);
    string stim(buff);
    return stim;
}

// 创建文件路径
void createFilePath()
{
    string dir = "log";
    // 检测文件是否存在
    if (access(dir.c_str(), F_OK))
    {
        int ret = mkdir(dir.c_str(), 0755);
        if (ret != 0)
            perror("create log path error!");
    }
}

// 创建log文件
int createLogFile(const string &fileName)
{
    int fid = open(fileName.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);
    if (fid < 0)
    {
        perror("log open error!");
    }
    return fid;
}

// 构造函数
Logger::Logger()
    : _fileId(-1), _enableOutputConsole(true)
{
    // 初始化互斥锁
    pthread_mutex_init(&_mtx, nullptr);
    // 创建文件夹
    createFilePath();
    // 按日期创建log文件
    if (_fileId < 0)
    {
        _fileName = "./log/log_" + getDatestr() + ".log";
        _fileId = createLogFile(_fileName);
    }
}

// 析构函数
Logger::~Logger()
{
    close(_fileId);
}

// 日志函数
void Logger::log(LogLevel level, int line, const char *format, ...)
{
    //[时间戳] [日志级别] [进程ID][线程ID] [文件名:行号] 消息内容

    // 时间戳
    char buff_time[80];
    time_t now_time = time(nullptr);
    struct tm *sttime = localtime(&now_time);
    strftime(buff_time, 80, "[%Y-%m-%d %X]", sttime);
    string cur_time = buff_time;
    // 日志等级
    string log_level;
    switch (level)
    {
    case DEBUG:
        log_level = "[DEBUG]";
        break;
    case INFO:
        log_level = "[INFO ]";
        break;
    case WARN:
        log_level = "[WARN ]";
        break;
    case ERROR:
        log_level = "[ERROR]";
        break;
    case FATAL:
        log_level = "[FATAL]";
        break;
    }

    // 获得当前进程id
    int pid = getpid();
    string spid = "[pid: " + to_string(pid) + "]";
    // 获得当前线程id
    pthread_t tid = pthread_self();
    std::stringstream ss;
    ss << std::hex << tid;
    string stid = "[tid: 0x" + ss.str() + "]";
    // 获得错误行号
    string sline = "[line: " + to_string(line) + "] :";
    // 日志内容
    char log_content[max_logsize];
    va_list arg;
    va_start(arg, format);
    vsnprintf(log_content, max_logsize, format, arg);
    va_end(arg);

    // 日志
    string log = cur_time + log_level + spid + stid + sline + log_content + "\r\n";

    // 输出日志
    pthread_mutex_lock(&_mtx); // 加锁
    if (_enableOutputConsole)
    {
        cout << log;
    }
    int ret = write(_fileId, log.c_str(), log.size());
    pthread_mutex_unlock(&_mtx); // 解锁
    if (ret < 0)
    {
        perror("log write error!");
    }
}

// 设置日志文件
void Logger::setOutputFile(const string &fileName, bool append)
{
    // 创建文件
    if (_fileId)
    {
        if (close(_fileId) < 0)
            perror("close fail");
    }
    if (append)
    {
        _fileId = open(fileName.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);
        if (_fileId < 0)
        {
            perror("log open error!");
        }
    }
    else
    {
        _fileId = open(fileName.c_str(), O_WRONLY | O_CREAT, 0666);
        if (_fileId < 0)
        {
            perror("log open error!");
        }
    }

    // 记录文件名
    _fileName = fileName;
}

// 设置是否追加
void Logger::setOutputConsole(bool enable)
{
    _enableOutputConsole = enable;
}

// 获取单例对象
Logger &Logger::Instance()
{
    static Logger log;
    return log;
}
